package com.sailfish.springbootdemo.service.db5;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.support.odps.udf.CodecCheck;
import com.sailfish.springbootdemo.dao.db2.*;
import com.sailfish.springbootdemo.dao.db4.UserDao;
import com.sailfish.springbootdemo.dao.db5.SfcScdpMSnDetailDao;
import com.sailfish.springbootdemo.pojo.Result;
import com.sailfish.springbootdemo.pojo.db2.*;
import com.sailfish.springbootdemo.pojo.db4.User;
import com.sailfish.springbootdemo.pojo.db5.SfcScdpMSnDetail;
import com.sailfish.springbootdemo.service.db2.ThirdApiService;
import com.sailfish.springbootdemo.utils.CommonUtils;
import com.sailfish.springbootdemo.utils.HttpUtil;
import com.sailfish.springbootdemo.utils.SfsUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;

import java.text.DecimalFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.Duration;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.Year;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.time.temporal.Temporal;
import java.util.*;
import java.util.stream.Collectors;
import lombok.extern.slf4j.Slf4j;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;

@Slf4j
@Component
@Service
public class SfcScdpMSnDetailServiceImpl implements SfcScdpMSnDetailService {

    private static final int BATCH_SIZE = 10000; // 每批处理记录数
    @Autowired
    private SfcScdpMSnDetailDao sfcScdpMSnDetailDao;
    @Autowired
    private SfcRateDao sfcRateDao;
    @Autowired
    private AxiConfigDao axiConfigDao;
    @Autowired
    private UserDao userDao;
    @Autowired
    private ThirdApiService thirdApiService;
    @Autowired
    SfsUtils sfsUtils;
    @Autowired
    AxiDataDao axiDataDao;
    @Autowired
    PcbCapacityDataApproveDao pcbCapacityDataApproveDao;
    @Autowired
    PcbSonFactoryInfoDao pcbSonFactoryInfoDao;
    @Autowired
    AxiDataCacheDao axiDataCacheDao;

    @Value("${personal.config.sfc.refreshAxiUrl}")
    private String refreshAxiUrl;
    @Value("${personal.config.sfc.getAllBoardTreeRelatedCompany}")
    private String getAllBoardTreeRelatedCompanyUrl;
    @Value("${personal.config.sfc.updateTouchanActualLinesAndSendEmail}")
    private String updateTouchanActualLinesAndSendEmailUrl;
    @Value("${third_url_prefix.axi-url}")
    private String getAxiApi;
    @Value("${personal.config.email.templateId}")
    private String templateCode;

    @Override
    @Scheduled(cron = "0 0 4 * * ?")// 每天凌晨4点 执行刷新AXI
    public void scheduledRefreshAxiData() {
        System.out.println("[scheduledRefreshAxiData] 定时任务开始执行: " + CommonUtils.formatCurrentTime());

        Integer refreshAxiCircle = 24;
        AxiConfig sfc_circle = axiConfigDao.getByAttrName("Axi_RefreshCircle", null);
        if (sfc_circle != null) {
            if (!StringUtils.isBlank(sfc_circle.getAttrValue())) {
                refreshAxiCircle = Integer.parseInt(sfc_circle.getAttrValue());
                System.out.println("refreshAxiCircle:" + refreshAxiCircle);
            }
        }

        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HHmmss");
        DateTimeFormatter formatter2 = DateTimeFormatter.ofPattern("yyyy-MM-dd");
        LocalDateTime lastExecutionDateTime = LocalDateTime.now();
        AxiConfig axiConfig = axiConfigDao.getByAttrName("lastSendMailTime_RefreshAxi", null);
        if (axiConfig != null) {
            String dateStr = axiConfig.getAttrValue();
            lastExecutionDateTime = LocalDateTime.parse(dateStr, formatter);
        } else {
            lastExecutionDateTime = LocalDateTime.parse("2024-01-01 000000", formatter);
        }
        long daysSinceLastExecution = ChronoUnit.HOURS.between(lastExecutionDateTime, LocalDateTime.now());
        System.out.println("daysSinceLastExecution:" + daysSinceLastExecution);
        if (daysSinceLastExecution >= refreshAxiCircle) {
            try {
                JSONObject jsonObject = new JSONObject();
                jsonObject.put("projectId", "");
                String res = HttpUtil.sendPostJson(refreshAxiUrl, jsonObject);
                JSONObject jsonObject2 = JSON.parseObject(res);
                if (jsonObject2.getInteger("Code") == 200) {
                    System.out.println((new Date()) + ":scheduledRefreshAxiData success");
                } else {
                    System.out.println((new Date()) + ":scheduledRefreshAxiData fail:" + jsonObject2.getString("Msg"));
                }
            } catch (Exception e) {
                System.out.println((new Date()) + ":scheduledRefreshAxiData exception:" + e.getMessage());
            }
            String newDate = LocalDateTime.now().format(formatter2) + " 040000";
            if (axiConfig == null) {
                AxiConfig axiConfig1 = new AxiConfig();
                axiConfig1.setAttrName("lastSendMailTime_RefreshAxi");
                axiConfig1.setAttrValue(newDate);
                axiConfig1.setStatus(4);
                axiConfig1.setRemark("定时提醒厂家审核通过邮件最后发送时间");
                axiConfigDao.insert(axiConfig1);
            } else {
                axiConfig.setAttrValue(newDate);
                axiConfigDao.update(axiConfig);
            }
        }
        System.out.println("[scheduledRefreshAxiData] 定时任务结束: " + CommonUtils.formatCurrentTime());
    }

    @Override
    @Scheduled(cron = "0 0 5 * * ?")
    public void scheduledSearchSfc() {
        System.out.println("[scheduledSearchSfc] 定时任务Start: " + CommonUtils.formatCurrentTime());
        try {
            String md5Sign = "6244832a706bbb2c81fb093367027f56";
            MultipartEntityBuilder params = MultipartEntityBuilder.create();
            params.addTextBody("signature", md5Sign);
            String res = null;
            try {
                res = HttpUtil.sendPostFormData(getAllBoardTreeRelatedCompanyUrl, params);
            } catch (Exception e) {
                e.printStackTrace();
            }
            JSONObject jsonObject = JSON.parseObject(res);
            if (jsonObject.getInteger("code") == 200) {
                JSONArray jsonArray = jsonObject.getJSONArray("data");
                JSONArray data = new JSONArray();
//                List<SfcScdpMSnDetail> sfcScdpMSnDetailList2 = sfcScdpMSnDetailDao.getByLimit2("0302A0108", "5C");
                for (int i = 0; i < jsonArray.size(); i++) {
                    JSONObject jo = jsonArray.getJSONObject(i);
                    Integer boardTreeRelatedCompanyId = jo.getInteger("boardTreeRelatedCompanyId");
                    String partNo = jo.getString("partNo");
                    String pcbaCompanyCode = jo.getString("pcbaCompanyCode");
                    List<SfcScdpMSnDetail> sfcScdpMSnDetailList = sfcScdpMSnDetailDao.getByLimit2(partNo, pcbaCompanyCode);
//                    String lines = "";
//                    if (sfcScdpMSnDetailList != null) {
//                        for (int j = 0; j < sfcScdpMSnDetailList.size(); j++) {
//                            lines += sfcScdpMSnDetailList.get(j).getLineCode() + ",";
//                        }
//                    }
//                    if (lines.length() > 0) {
//                        lines = lines.substring(0, lines.length() - 1);
//                    }
                    JSONObject joNew = new JSONObject();
                    joNew.put("actualLines",sfcScdpMSnDetailList);
                    joNew.put("boardTreeRelatedCompanyId",boardTreeRelatedCompanyId);
                    data.add(joNew);
                }
                MultipartEntityBuilder params2 = MultipartEntityBuilder.create();
                params2.addTextBody("signature", md5Sign);
                params2.addTextBody("json", data.toString());
                String res2 = null;
                try {
                    res2 = HttpUtil.sendPostFormData(updateTouchanActualLinesAndSendEmailUrl, params2);
                    System.out.println("[scheduledSearchSfc] 请求updateTouchanActualLinesAndSendEmailUrl接口返回: " + res2);
                    JSONObject jsonObject2 = JSON.parseObject(res2);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            System.out.println("[scheduledSearchSfc] 定时任务结束End: " + CommonUtils.formatCurrentTime());
        } catch (Exception e) {
            System.out.println("[scheduledSearchSfc] 定时任务Exception: " + e.getMessage());
        }
    }
    @Value("${personal.config.sso.trusted-redirect-uri}")
    private String trustedRedirectUri;

    @Override
    @Scheduled(cron = "0 0 6 * * ?") // 每天凌晨6点执行 发邮件给AXI厂家
    public void scheduledSendAxiMailToFactory() {
        System.out.println("[scheduledSendAxiMailToFactory] 定时任务开始执行: " + CommonUtils.formatCurrentTime());
        log.info("[scheduledSendAxiMailToFactory] 发邮件给AXI厂家定时任务开始执行: " + CommonUtils.formatCurrentTime());
        Integer axiMailCircle = 7;
        AxiConfig axi_Mailcircle = axiConfigDao.getByAttrName("Axi_MailCircle", null);
        if (axi_Mailcircle != null) {
            if (!StringUtils.isBlank(axi_Mailcircle.getAttrValue())) {
                axiMailCircle = Integer.parseInt(axi_Mailcircle.getAttrValue());
                System.out.println("axiSendMailToFactoryCircle:" + axiMailCircle);
                log.info("axiSendMailToFactoryCircle:" + axiMailCircle);
            }
        }
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HHmmss");
        DateTimeFormatter formatter2 = DateTimeFormatter.ofPattern("yyyy-MM-dd");
        LocalDateTime lastExecutionDateTime = LocalDateTime.now();
        AxiConfig axiConfig = axiConfigDao.getByAttrName("lastSendMailTime_ToFactory", null);
        if (axiConfig != null) {
            String dateStr = axiConfig.getAttrValue();
            lastExecutionDateTime = LocalDateTime.parse(dateStr, formatter);
        } else {
            lastExecutionDateTime = LocalDateTime.parse(Year.now() + "-01-01 000000", formatter);
        }
        long daysSinceLastExecution = ChronoUnit.DAYS.between(lastExecutionDateTime, LocalDateTime.now());
        System.out.println("daysSinceLastExecution:" + daysSinceLastExecution);
        log.info("daysSinceLastExecution:" + daysSinceLastExecution);
        if (daysSinceLastExecution >= axiMailCircle) {
            try {
                String userStr = "";
                AxiConfig factoryMailTo = axiConfigDao.getByAttrName("Axi_FactoryMailTo", null);
                if (factoryMailTo != null) {
                    if (!StringUtils.isBlank(factoryMailTo.getAttrValue())) {
                        userStr = factoryMailTo.getAttrValue();
                        System.out.println("Axi_FactoryMailTo:" + userStr);
                        log.info("Axi_FactoryMailTo:" + userStr);
                    }
                }
                if (!StringUtils.isBlank(userStr)) {
                    String title = "AXI测试要求最新审核通过编码通知";
                    String mailContent = "";
                    Integer priority = 1;   //优先级，1普通，2有限
                    String sendMail = "";
                    String[] arrUser = userStr.split(",");
                    List<MailAddress> mailAddressList = new ArrayList<MailAddress>();//收件列表
                    for (int j = 0; j < arrUser.length; j++) {
                        if (!StringUtils.isBlank(arrUser[j])) {
                            Integer userId = Integer.parseInt(arrUser[j]);
                            User userInfoById = userDao.getUserInfoById(userId);
                            if(Objects.isNull(userInfoById))
                            {
                                // 用户不存在，直接下一次循环
                                continue;
                            }
                            if (!StringUtils.isBlank(userInfoById.getEmail())) {
                                // 获取用户邮箱信息
                                String emailString = userInfoById.getEmail();

                                // 分割邮箱字符串，得到各个邮箱地址
                                String[] emailArray = emailString.split(",");

                                // 遍历每个邮箱地址并添加到 mailAddressList 中
                                for (String email : emailArray) {
                                    if(StringUtils.isEmpty(email))
                                    {
                                        // 空邮件不处理
                                        continue;
                                    }
                                    // 去除可能存在的前后空格
                                    String trimmedEmail = email.trim();
                                    log.info("email address is " + trimmedEmail);
                                    System.out.println("email address is " + trimmedEmail);
                                    // 检查邮箱是否为空或无效（可选）
                                    if (!trimmedEmail.isEmpty()) {
                                        MailAddress mailAddress = new MailAddress();
                                        mailAddress.setMailAddress(trimmedEmail);
                                        mailAddress.setType(0);
                                        mailAddressList.add(mailAddress);
                                    }
                                }
                            }
                        }
                    }
                    List<AxiData> all = axiDataDao.getAll();
                    System.out.println("AxiData all:" + JSONObject.toJSONString(all));
                    List<AxiData> needSendMail = axiDataDao.getNeedSendMail();
                    System.out.println("needSendMail count:" + needSendMail.size());
                    String linkUrl = trustedRedirectUri + "/toolsManage/axiTest";
                    if (needSendMail != null && needSendMail.size() > 0) {
                        String htmlContent = "<html>" +
                                "<body>" +
                                "<p>AXI最新审核通过的新增/刷新编码清单汇总</p>" +
                                "<p>网页链接:<a href='"+ linkUrl + "'>点击跳转</a></p>" +
                                "<table style='width:100%' border='1' cellspacing='1'>";
                        htmlContent += "<tr>" +
                                "<th>表名</th>" +
                                "<th>测试比例</th>" +
                                "<th>测试项目</th>" +
                                "<th>产品级别</th>" +
                                "<th>产品名称</th>" +
                                "<th>产品线</th>" +
                                "<th>印制板编码</th>" +
                                "<th>制成板编码</th>" +
                                "<th>制成板板名</th>" +
                                "<th>产品状态</th>" +
                                "<th>单板复杂度</th>" +
                                "<th>直通率</th>" +
                                "<th>备注</th>" +
                                "</tr>";

                        for (int n = 0; n < needSendMail.size(); n++) {
                            htmlContent += "<tr>" +
                                    "<th>" + (needSendMail.get(n).getTableType() == null ? "" : needSendMail.get(n).getTableType()) + "</th>" +
                                    "<th>" + (needSendMail.get(n).getTestRate() == null ? "" : needSendMail.get(n).getTestRate()) + "</th>" +
                                    "<th>" + (needSendMail.get(n).getTestProject() == null ? "" : needSendMail.get(n).getTestProject()) + "</th>" +
                                    "<th>" + (needSendMail.get(n).getProductLevel() == null ? "" : needSendMail.get(n).getProductLevel()) + "</th>" +
                                    "<th>" + (needSendMail.get(n).getProductName() == null ? "" : needSendMail.get(n).getProductName()) + "</th>" +
                                    "<th>" + (needSendMail.get(n).getProductLine() == null ? "" : needSendMail.get(n).getProductLine()) + "</th>" +
                                    "<th>" + (needSendMail.get(n).getPcbCode() == null ? "" : needSendMail.get(n).getPcbCode()) + "</th>" +
                                    "<th>" + (needSendMail.get(n).getPanelCode() == null ? "" : needSendMail.get(n).getPanelCode()) + "</th>" +
                                    "<th>" + (needSendMail.get(n).getPanelName() == null ? "" : needSendMail.get(n).getPanelName()) + "</th>" +
                                    "<th>" + (needSendMail.get(n).getProductStatus() == null ? "" : needSendMail.get(n).getProductStatus()) + "</th>" +
                                    "<th>" + (needSendMail.get(n).getBoardComplexity() == null ? "" : needSendMail.get(n).getBoardComplexity()) + "</th>" +
                                    "<th>" + (needSendMail.get(n).getFirstPassYield() == null ? "" : needSendMail.get(n).getFirstPassYield()) + "</th>" +
                                    "<th>" + (needSendMail.get(n).getRemark() == null ? "" : needSendMail.get(n).getRemark()) + "</th>" +
                                    "</tr>";
                        }

                        htmlContent += "</table>" +
                                "</body>" +
                                "</html>";
                        mailContent += htmlContent;
                        Result result = thirdApiService.sendMail("", title, title, mailContent, templateCode, mailAddressList, null, null, null, null, priority, sendMail);
                        log.info("sendMail result code:" + result.getCode() + " msg:" + result.getMsgCn() + " data" + result.getData());
                        if (result.getCode() == 200) {
                            log.info("sendMail api response success");
                            for (int n = 0; n < needSendMail.size(); n++) {
                               Integer tt = axiDataDao.update(needSendMail.get(n));
                               log.info("affected rows :" + tt);
                            }
                            log.info((new Date()) + ":scheduledSendAxiMailToFactory success");
                        } else {
                            log.error((new Date()) + ":scheduledSendAxiMailToFactory failed");
                        }
                    }
                }

                String newDate = LocalDateTime.now().format(formatter2) + " 060000";
                if (axiConfig == null) {
                    AxiConfig axiConfig1 = new AxiConfig();
                    axiConfig1.setAttrName("lastSendMailTime_ToFactory");
                    axiConfig1.setAttrValue(newDate);
                    axiConfig1.setStatus(4);
                    axiConfig1.setRemark("定时提醒厂家审核通过邮件最后发送时间");
                    axiConfigDao.insert(axiConfig1);
                } else {
                    axiConfig.setAttrValue(newDate);
                    axiConfigDao.update(axiConfig);
                }
            } catch (Exception e) {
                e.printStackTrace();
                log.error((new Date()) + ":scheduledSendAxiMailToFactory exception:", e);
            }
        }
        log.info("[scheduledSendAxiMailToFactory] 定时任务结束: " + CommonUtils.formatCurrentTime());
    }

    @Override
    @Scheduled(cron = "0 0 7 * * ?") // 每天凌晨7点执行 刷新0302编码信息
    public void scheduleRefreshAll0301CodeInfo()
    {
        System.out.println("[schduleRefreshAll0301CodeInfo] 定时任务开始执行: " + CommonUtils.formatCurrentTime());
        String refreshAll0301CodeInfoApi = getAxiApi + "/api/Axi/refreshAxiDataInfo";
        try {
            System.out.println("[schduleRefreshAll0301CodeInfo] 接口请求地址：" + refreshAll0301CodeInfoApi);
            JSONObject jsonObject = new JSONObject();
            String res = HttpUtil.sendPostJson(refreshAll0301CodeInfoApi, jsonObject);
            JSONObject jsonObject2 = JSON.parseObject(res);
            if (jsonObject2.getInteger("Code") == 200)
            {
                System.out.println((new Date()) + ":schduleRefreshAll0301CodeInfo success");
            }
            else
            {
                System.out.println((new Date()) + ":schduleRefreshAll0301CodeInfo fail:" + jsonObject2.getString("Msg"));
            }
        }
        catch (Exception e) {
            System.out.println((new Date()) + ":schduleRefreshAll0301CodeInfo exception:" + e.getMessage());
        }
        System.out.println("[schduleRefreshAll0301CodeInfo] 定时任务结束: " + CommonUtils.formatCurrentTime());
    }


    @Override
//    @Scheduled(cron = "0 0 7 * * ?") // 每天凌晨7点执行 发邮件给AXI待审核人员
    public void scheduledSendAxiMailToApprover() {
        System.out.println("[scheduledSendAxiMailToApprover] 定时任务开始执行: " + CommonUtils.formatCurrentTime());
        Integer axiMailCircle = 1;
        AxiConfig axiMailCircleToApprover = axiConfigDao.getByAttrName("Axi_MailCircle_ToApprover", null);
        if (axiMailCircleToApprover != null) {
            if (!StringUtils.isBlank(axiMailCircleToApprover.getAttrValue())) {
                axiMailCircle = Integer.parseInt(axiMailCircleToApprover.getAttrValue());
                System.out.println("axiSendMailToApproverCircle:" + axiMailCircle);
            }
        }
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HHmmss");
        DateTimeFormatter formatter2 = DateTimeFormatter.ofPattern("yyyy-MM-dd");
        LocalDateTime lastExecutionDateTime = LocalDateTime.now();
        AxiConfig axiConfig = axiConfigDao.getByAttrName("lastSendMailTime", null);
        if (axiConfig != null) {
            String dateStr = axiConfig.getAttrValue();
            lastExecutionDateTime = LocalDateTime.parse(dateStr, formatter);
        } else {
            lastExecutionDateTime = LocalDateTime.parse("2024-01-01 000000", formatter);
        }

        long daysSinceLastExecution = ChronoUnit.DAYS.between(lastExecutionDateTime, LocalDateTime.now());
        if (daysSinceLastExecution >= axiMailCircle) {
            try {
                List<String> approveUserList = axiDataCacheDao.getApproveUserList();

                for (int i = 0; i < approveUserList.size(); i++) {
                    String userStr = approveUserList.get(i);
                    String title = "AXI新增/刷新编码待审核提醒";
                    String mailContent = "";
                    Integer priority = 1;   //优先级，1普通，2有限
                    String sendMail = "";
                    List<MailAddress> mailAddressList = new ArrayList<MailAddress>();//收件列表
                    if (!StringUtils.isBlank(userStr)) {
                        String[] arrUser = userStr.split(",");
                        for (int j = 0; j < arrUser.length; j++) {
                            if (!StringUtils.isBlank(arrUser[j])) {
                                Integer userId = Integer.parseInt(arrUser[j]);
                                User userInfoById = userDao.getUserInfoById(userId);
                                if (userInfoById != null && !StringUtils.isBlank(userInfoById.getEmail())) {
                                    MailAddress mailAddress1 = new MailAddress();
                                    mailAddress1.setMailAddress(userInfoById.getEmail());
                                    mailAddress1.setType(0);
                                    mailAddressList.add(mailAddress1);
                                }
                            }
                        }
                    }
                    String linkUrl = trustedRedirectUri + "/toolsManage/axiAudit";
                    List<AxiDataCache> axiDataCacheList = axiDataCacheDao.getByApproveUser(userStr, "待审核");
                    if (axiDataCacheList != null && axiDataCacheList.size() > 0) {
                        String htmlContent = "<html>" +
                                "<body>" +
                                "<p>以下是AXI新增/刷新还未审核的编码，请各位责任人尽快处理:</p>" +
                                "<p>网页链接:<a href='"+ linkUrl + "'>点击跳转</a></p>" +
                                "<table style='width:100%' border='1' cellspacing='1'>";
                        htmlContent += "<tr>" +
                                "<th>新表名</th>" +
                                "<th>原表名</th>" +
                                "<th>新测试比例</th>" +
                                "<th>原测试比例</th>" +
                                "<th>新测试项目</th>" +
                                "<th>原测试项目</th>" +
                                "<th>新产品级别</th>" +
                                "<th>原产品级别</th>" +
                                "<th>产品名称</th>" +
                                "<th>产品线</th>" +
                                "<th>印制板编码</th>" +
                                "<th>制成板编码</th>" +
                                "<th>制成板板名</th>" +
                                "<th>新产品状态</th>" +
                                "<th>原产品状态</th>" +
                                "<th>单板复杂度</th>" +
                                "<th>新直通率</th>" +
                                "<th>原直通率</th>" +
                                "<th>刷新类型</th>" +
                                "<th>刷新备注</th>" +
                                "<th>审核状态</th>" +
                                "</tr>";
                        for (int j = 0; j < axiDataCacheList.size(); j++) {
                            AxiDataCache axiDataCache = axiDataCacheList.get(j);
                            htmlContent += "<tr>" +
                                    "<th>" + axiDataCache.getTableType() + "</th>" +
                                    "<th>" + (axiDataCache.getTableTypeOld() == null ? "" : axiDataCache.getTableTypeOld()) + "</th>" +
                                    "<th>" + (axiDataCache.getTestRate() == null ? "" : axiDataCache.getTestRate()) + "</th>" +
                                    "<th>" + (axiDataCache.getTestRateOld() == null ? "" : axiDataCache.getTestRateOld()) + "</th>" +
                                    "<th>" + (axiDataCache.getTestProject() == null ? "" : axiDataCache.getTestProject()) + "</th>" +
                                    "<th>" + (axiDataCache.getTestProjectOld() == null ? "" : axiDataCache.getTestProjectOld()) + "</th>" +
                                    "<th>" + (axiDataCache.getProductLevel() == null ? "" : axiDataCache.getProductLevel()) + "</th>" +
                                    "<th>" + (axiDataCache.getProductLevelOld() == null ? "" : axiDataCache.getProductLevelOld()) + "</th>" +
                                    "<th>" + (axiDataCache.getProductName() == null ? "" : axiDataCache.getProductName()) + "</th>" +
                                    "<th>" + (axiDataCache.getProductLine() == null ? "" : axiDataCache.getProductLine()) + "</th>" +
                                    "<th>" + (axiDataCache.getPcbCode() == null ? "" : axiDataCache.getPcbCode()) + "</th>" +
                                    "<th>" + (axiDataCache.getPanelCode() == null ? "" : axiDataCache.getPanelCode()) + "</th>" +
                                    "<th>" + (axiDataCache.getPanelName() == null ? "" : axiDataCache.getPanelName()) + "</th>" +
                                    "<th>" + (axiDataCache.getProductStatus() == null ? "" : axiDataCache.getProductStatus()) + "</th>" +
                                    "<th>" + (axiDataCache.getProductStatusOld() == null ? "" : axiDataCache.getProductStatusOld()) + "</th>" +
                                    "<th>" + (axiDataCache.getBoardComplexity() == null ? "" : axiDataCache.getBoardComplexity()) + "</th>" +
                                    "<th>" + (axiDataCache.getFirstPassYield() == null ? "" : axiDataCache.getFirstPassYield()) + "</th>" +
                                    "<th>" + (axiDataCache.getFirstPassYieldOld() == null ? "" : axiDataCache.getFirstPassYieldOld()) + "</th>" +
                                    "<th>" + (axiDataCache.getRefreshType() == null ? "" : axiDataCache.getRefreshType()) + "</th>" +
                                    "<th>" + (axiDataCache.getRemark() == null ? "" : axiDataCache.getRemark()) + "</th>" +
                                    "<th>" + (axiDataCache.getApproveStatus() == null ? "" : axiDataCache.getApproveStatus()) + "</th>" +
                                    "</tr>";
                        }
                        htmlContent += "</table>" +
                                "</body>" +
                                "</html>";
                        mailContent += htmlContent;
                        Result result = thirdApiService.sendMail("", title, title, mailContent, templateCode, mailAddressList, null, null, null, null, priority, sendMail);
                        if (result.getCode() == 200) {
                            System.out.println((new Date()) + ":scheduledSendAxiMailToApprover success");
                        } else {
                            System.out.println((new Date()) + ":scheduledSendAxiMailToApprover fail:" + result.getMsgCn());
                        }
                    }

                }

                String newDate = LocalDateTime.now().format(formatter2) + " 070000";
                if (axiConfig == null) {
                    AxiConfig axiConfig1 = new AxiConfig();
                    axiConfig1.setAttrName("lastSendMailTime");
                    axiConfig1.setAttrValue(newDate);
                    axiConfig1.setStatus(4);
                    axiConfig1.setRemark("定时提醒审核邮件最后发送时间");
                    axiConfigDao.insert(axiConfig1);
                } else {
                    axiConfig.setAttrValue(newDate);
                    axiConfigDao.update(axiConfig);
                }
            } catch (Exception e) {
                e.printStackTrace();
                System.out.println((new Date()) + ":scheduledSendAxiMailToFactory exception:" + e.getMessage());
            }
        }
        System.out.println("[scheduledSendAxiMailToApprover] 定时任务结束: " + CommonUtils.formatCurrentTime());
    }


    @Override
    @Scheduled(cron = "0 0 0 * * ?") // 每小时整点 执行一次检查是否要计算直通率
    @Transactional(transactionManager = "MysqlTransactionManager2")
    public void scheduledCalculateFirstPassRate() {
        LocalDateTime tmp = LocalDateTime.now();
        //保证是整点
        LocalDateTime now = tmp.withMinute(0).withSecond(0).withNano(0);
        System.out.println("[scheduledCalculateFirstPassRate] 定时任务开始执行: " + CommonUtils.formatCurrentTime());
        log.info("[scheduledCalculateFirstPassRate] 定时任务开始执行: " + CommonUtils.formatCurrentTime());
        try {
            AxiConfig sfc_circle = axiConfigDao.getByAttrName("SFC_Circle", null);
            Integer circle = 24; // 默认周期为24小时

            if (sfc_circle != null && !StringUtils.isBlank(sfc_circle.getAttrValue())) {
                circle = Integer.parseInt(sfc_circle.getAttrValue());
                System.out.println("[scheduledCalculateFirstPassRate] circle:" + circle);
                log.info("[scheduledCalculateFirstPassRate] circle:" + circle);
            }

            // 获取上次执行时间，从配置表获取上次执行时间
            AxiConfig lastExecutionTimeConfig = axiConfigDao.getByAttrName("SFC_Last_Calculate_Rate", null);
            if(lastExecutionTimeConfig == null)
            {
                System.out.println("[scheduledCalculateFirstPassRate] SFC_Last_Calculate_Rate value is null");
                return;
            }
            // 定义与字符串格式相匹配的 DateTimeFormatter
            DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
            if (lastExecutionTimeConfig != null && !StringUtils.isBlank(lastExecutionTimeConfig.getAttrValue())) {
                // 使用自定义的 formatter 解析字符串
                LocalDateTime lastExecutionTime = LocalDateTime.parse(lastExecutionTimeConfig.getAttrValue(), formatter);
                System.out.println("[scheduledCalculateFirstPassRate] last calculate:" + lastExecutionTime);
                log.info("[scheduledCalculateFirstPassRate] last calculate:" + lastExecutionTime);
            }
            // 使用自定义的 formatter 解析字符串
            LocalDateTime lastExecutionTime = LocalDateTime.parse(lastExecutionTimeConfig.getAttrValue(), formatter);
            // 如果从未执行过或者距离上次执行已经过了一个周期，则执行计算
            long duration = Duration.between(lastExecutionTime, now).toHours();
            log.info("[scheduledCalculateFirstPassRate] duration to hours:" + duration);
            if (duration >= circle) {
                System.out.println("[scheduledCalculateFirstPassRate] trigger calculateFirstPassRate, time:" + now);
                // 使用 formatter 将 LocalDateTime 转换为 String
                String formattedDate = now.format(formatter);
                lastExecutionTimeConfig.setAttrName("SFC_Last_Calculate_Rate");
                lastExecutionTimeConfig.setAttrValue(formattedDate);
                lastExecutionTimeConfig.setUpdateTime(new Date());
                calculateFirstPassRate();
                System.out.println("[scheduledCalculateFirstPassRate] 定时任务结束, time:" + CommonUtils.formatCurrentTime());
                log.info("[scheduledCalculateFirstPassRate] 定时任务结束: " + CommonUtils.formatCurrentTime());
            }
            axiConfigDao.update(lastExecutionTimeConfig);// 更新最后执行时间为当前时间
        } catch (Exception e) {
            // 异常回滚
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            log.info("[scheduledCalculateFirstPassRate] 定时任务异常结束: " + CommonUtils.formatCurrentTime());
            System.out.println("Error occurred during first pass rate calculation: " + e.getMessage());
            e.printStackTrace();
        }
    }

    public void calculateFirstPassRate() {
        Integer axiComplex = getConfiguredValue("Axi_Complex", 0);
        Date startDate = getStartDate();
        List<String> codeList = axiDataDao.getSfcCodeList(new Double(axiComplex)).stream()
                .map(AxiData::getPanelCode)
                .collect(Collectors.toList());
        log.info("calculateFirstPassRate codeList size is " + codeList.size());
        for (String code : codeList) {
            log.info("calculateFirstPassRate code is " + code);
            processCode(code, startDate);
        }
    }

    private int getNextSortValueForCode(String code) {
        Integer maxSort = sfcRateDao.getMaxSortByCode(code);
        return (maxSort == null) ? 1 : maxSort + 1;
    }


    //根据AXI特殊测试要求的编码清单，从SFC系统中抓取SN、制程板编码、工序组、过站时间、BT_FLAG、ERR_FLAG、FIRST_PASS的所有过站数据
    // 判断是否达到了直通率计算的数量条件（默认为每1000面计算一次直通率，该数量可自主配置）
    // 自系统上线时间开始，该编码加工数量默认为0
    // 以该编码的SN为统计维度，首次过站（FIRST_PASS=0）、工序组为SMT为筛选条件筛选数据
    // 在上一步数据基础上，BT_FLAG为空情况下，数量+1； BT_FLAG非空的情况下（B或T），BT__FLAG = B，数量+1或BT__FLAG = T，数量+1
    // 若该编码加工面数量>= 1000，则进入下一步计算直通率。数量1000若修改，则自动刷新为修改后的数量
    private void processCode(String code, Date startDate) {
        try {
            log.info("Processing start");
            SfcRate sfcRateOld = sfcRateDao.getByCode(code);
            Date lastInStationTime = (sfcRateOld != null) ? sfcRateOld.getLastInstationTime() : startDate;
            int totalProcessed = (sfcRateOld != null) ? sfcRateOld.getDenominator() : 0;
            int errCount = (sfcRateOld != null) ? sfcRateOld.getMolecule() : 0;
            int oldRateId = (sfcRateOld != null) ? sfcRateOld.getId() : 0;
            String serialNum = (sfcRateOld != null) ? sfcRateOld.getSerialNum() : null;

            Map<String, Integer> serialNumberCounts = new HashMap<>();
            Set<String> defectiveSNs = new HashSet<>();
            String lastSerialNum = null;
            Date currentLastInStationTime = lastInStationTime;
            Integer currentQty = getConfiguredValue("SFC_Qty", 1000);
            if (currentQty == null) currentQty = 1000;

            if(totalProcessed == currentQty)
            {
                // 避免重复插入冗余完整批次
                totalProcessed = 0;
                errCount = 0;
            }

            int offset = 0;
            List<SfcScdpMSnDetail> batch;
            batch = sfcScdpMSnDetailDao.getByLimit(code, lastInStationTime, offset, BATCH_SIZE);
            while (!batch.isEmpty()) {
                // ✅ 去重逻辑：只有在 batch > 1 时才移除重复 SN
                if (serialNum != null && batch != null && batch.size() > 0) {
                    SfcScdpMSnDetail first = batch.get(0);
                    if (serialNum.equals(first.getSerialNumber())) {
                        batch.remove(0);
                        log.info("Removed duplicated serialNum {} from batch head for code {}", serialNum, code);
                    }
                }

                log.info("Processing batch for code: {}, batch size: {}", code, batch.size());

                for (SfcScdpMSnDetail detail : batch) {
                    String sn = detail.getSerialNumber();
                    String btFlag = detail.getBtFlag();
                    String errFlag = detail.getErrFlag();
                    String firstPass = detail.getFirstPass();
                    String factoryGroupCode = detail.getFactoryGroupCode();
                    serialNumberCounts.put(sn, serialNumberCounts.getOrDefault(sn, 0) + 1);

                    // ✅ 加工数量逻辑
                    log.info("current code: {}, firstPass: {}, factoryGroupCode: {}, btFlag: {}, sn count: {}",
                            code, firstPass, factoryGroupCode, btFlag, serialNumberCounts.get(sn));
                        if (StringUtils.isEmpty(btFlag) || "B".equalsIgnoreCase(btFlag) || "T".equalsIgnoreCase(btFlag)) {
                            // 一个双面板最多统计两次
                            if (serialNumberCounts.get(sn) <= 2) {
                                totalProcessed++;
                                log.info("Incremented totalProcessed for SN: {}, code: {}, btFlag: {}, count: {}",
                                        sn, code, btFlag, serialNumberCounts.get(sn));
                            }
                        }
                        // 一个面板最多统计一次缺陷
                        if ("F".equalsIgnoreCase(errFlag) && !defectiveSNs.contains(sn)) {
                            defectiveSNs.add(sn);
                            errCount++;
                            log.info("Incremented errCount for SN: {}, code: {}, btFlag: {}, errCount: {}",
                                    sn, code, btFlag, errCount);
                        }

                    lastSerialNum = sn;
                    currentLastInStationTime = detail.getInStationTime();
                    // 实时获取qty值，批次计算的分母同步调整
                    Integer newQty = getConfiguredValue("SFC_Qty", 1000);
                    if (newQty == null) newQty = 1000;

                    if (!newQty.equals(currentQty)) {
                        log.info("Qty changed for code: {}, from {} to {}", code, currentQty, newQty);
                        if (totalProcessed >= currentQty) {
                            insertFullBatch(code, currentQty, errCount, currentLastInStationTime, lastSerialNum);
                            totalProcessed -= currentQty;
                            errCount = 0;
                            defectiveSNs.clear();
                            oldRateId = 0;
                            sfcRateOld = null;
                            log.info("Inserted full batch for code: {}, qty: {}, remaining totalProcessed: {}",
                                    code, currentQty, totalProcessed);
                        }
                        currentQty = newQty;
                    }

                    while (totalProcessed >= currentQty) {
                        insertFullBatch(code, currentQty, errCount, currentLastInStationTime, lastSerialNum);
                        totalProcessed -= currentQty;
                        errCount = 0;
                        defectiveSNs.clear();
                        // 一旦插入完整批记录，删除旧尾批记录
                        if(sfcRateOld != null)
                        {
                            if (sfcRateOld.getDenominator() < currentQty) {
                                sfcRateDao.deleteById(sfcRateOld.getId());
                                log.info("删除旧记录，code: {}, denominator < {}", code, currentQty);
                            }
                        }
                        oldRateId = 0;
                        sfcRateOld = null;
                        log.info("Inserted full batch for code: {}, qty: {}, remaining totalProcessed: {}",
                                code, currentQty, totalProcessed);
                    }
                }

                if (batch != null && !batch.isEmpty()) {
                    // ✅ 防御性赋值，确保即使不进入 for 循环也能赋值 lastSerialNum
                    lastSerialNum = batch.get(batch.size() - 1).getSerialNumber();
                }
                offset += BATCH_SIZE;
                batch = sfcScdpMSnDetailDao.getByLimit(code, lastInStationTime, offset, BATCH_SIZE);
                log.info("offset is " + offset);
            }

            // ✅ 尾批次处理逻辑
            if (totalProcessed > 0) {
                log.info("Before updateOrInsertTailBatch: lastSerialNum = {}", lastSerialNum);
                updateOrInsertTailBatch(code, totalProcessed, errCount, currentLastInStationTime, lastSerialNum, oldRateId, sfcRateOld);
                log.info("Processed tail batch for code: {}, denominator: {}", code, totalProcessed);
            }
        } catch (Exception e) {
            log.error("Error processing code {}: {}", code, e.getMessage(), e);
        }
    }

    private void insertFullBatch(String code, int qty, int errCount, Date lastInStationTime, String serialNum) {
        // 先删除上一批满批
//        int pre = getNextSortValueForCode(code) - 1;
//        if(pre > 0)
//        {
//            sfcRateDao.deleteOldRateBySort(code, pre);
//        }
        SfcRate sfcRate = new SfcRate();
        sfcRate.setCode(code);
        sfcRate.setCreateTime(Date.from(Instant.now()));
        sfcRate.setLastInstationTime(lastInStationTime);
        sfcRate.setDenominator(qty);
        sfcRate.setMolecule(errCount);
        sfcRate.setSerialNum(serialNum);
        sfcRate.setRate(calculateRate(qty, errCount));
        sfcRate.setSort(getNextSortValueForCode(code)); // 设置 sort
        sfcRateDao.insert(sfcRate);
        log.info("Inserted full batch: code={}, serialNum={}, rate={}, sort={}", code, serialNum, sfcRate.getRate(), sfcRate.getSort());
    }

    public void updateOrInsertTailBatch(String code,
                                        int totalProcessed,
                                        int errCount,
                                        Date currentLastInStationTime,
                                        String lastSerialNum,
                                        int oldRateId,
                                        SfcRate sfcRateOld) {

        // ✅ 获取数据库中当前最新一条记录
        SfcRate latest = sfcRateDao.getLatestByCode(code);

        // ✅ 如果数据库中已有完全相同的一条记录，则跳过
        if (latest != null &&
                latest.getDenominator() == totalProcessed &&
                lastSerialNum != null &&
                lastSerialNum.equals(latest.getSerialNum())) {
            log.info("⏩ Skip duplicate record insert: code={}, SN={}", code, lastSerialNum);
            return;
        }

        // ✅ 正常插入或更新尾批逻辑
        SfcRate tailRate = new SfcRate();
        tailRate.setCode(code);
        tailRate.setCreateTime(Date.from(Instant.now()));
        tailRate.setLastInstationTime(currentLastInStationTime);
        tailRate.setDenominator(totalProcessed);
        tailRate.setMolecule(errCount);
        tailRate.setSerialNum(lastSerialNum);
        tailRate.setRate(calculateRate(totalProcessed, errCount));

        if (oldRateId > 0 && sfcRateOld != null) {
            tailRate.setId(oldRateId);
            tailRate.setCreateTime(sfcRateOld.getCreateTime());
            tailRate.setSort(sfcRateOld.getSort()); // 保持原顺序
            sfcRateDao.update(tailRate);
            log.info("✅ 更新尾批：code={}, SN={}, total={}, molecule={}", code, lastSerialNum, totalProcessed, errCount);
        } else {
            tailRate.setSort(getNextSortValueForCode(code));
            sfcRateDao.insert(tailRate);
            log.info("✅ 插入尾批：code={}, SN={}, total={}, molecule={}", code, lastSerialNum, totalProcessed, errCount);
        }
    }

    private String calculateRate(int denominator, int molecule) {
        if (denominator == 0) return "0.00";
        double rate = (1 - (double) molecule / denominator) * 100;
        log.info("Calculating rate: molecule={}, denominator={}, rate={}", molecule, denominator, rate);
        return new DecimalFormat("#.00").format(rate);
    }

    private Date getStartDate() {
        try {
            return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse("2022-01-01 00:00:00");
        } catch (ParseException e) {
            log.error("解析起始日期失败", e);
            throw new RuntimeException("无效的起始日期", e);
        }
    }

    private Integer getConfiguredValue(String configName, Integer defaultValue) {
        AxiConfig config = axiConfigDao.getByAttrName(configName, null);
        return config != null && !StringUtils.isBlank(config.getAttrValue()) ?
                Integer.parseInt(config.getAttrValue()) : defaultValue;
    }

//    public void scheduledCalculateFirstPassRate() {
////        DecimalFormat df1 = new DecimalFormat("#.00");
////        double rate01 = (1 - 29 * 1.0  / 1000) * 1.0 * 100;
////        String rate1 = df1.format(rate01);
//
//        Integer circle = 24;
//        Timer timer = new Timer();
//        AxiConfig sfc_circle = axiConfigDao.getByAttrName("SFC_Circle", null);
//        if (sfc_circle != null) {
//            if (!StringUtils.isBlank(sfc_circle.getAttrValue())) {
//                circle = Integer.parseInt(sfc_circle.getAttrValue());
//                System.out.println("circle:" + circle);
//            }
//        }
//        long period = 1000 * 60 * 60 * circle;
//        timer.schedule(new TimerTask() {
//            @Override
//            public void run() {
//                try {
//                    calculateFirstPassRate();
//                    Thread.sleep(period);
//                } catch (Exception e) {
//                    e.printStackTrace();
//                }
//            }
//        }, 1000, period);
//    }
//
//    public void calculateFirstPassRate() throws ParseException {
//        Integer qty = 1000;
//        AxiConfig sfc_qty = axiConfigDao.getByAttrName("SFC_Qty", null);
//        if (sfc_qty != null) {
//            if (!StringUtils.isBlank(sfc_qty.getAttrValue())) {
//                qty = Integer.parseInt(sfc_qty.getAttrValue());
//                System.out.println("qty:" + qty);
//                System.out.println("calculateFirstPassRate");
//            }
//        }
//        Map<String, String> map = new HashMap<>();
//        List<String> codeList;
//        List<AxiData> sfcCodeList = axiDataDao.getSfcCodeList();
//        codeList = sfcCodeList.stream().map(p->p.getPanelCode()).collect(Collectors.toList());
//        //根据AXI特殊测试要求的编码清单，从SFC系统中抓取SN、制程板编码、工序组、过站时间、BT_FLAG、ERR_FLAG、FIRST_PASS的所有过站数据
//        // 判断是否达到了直通率计算的数量条件（默认为每1000面计算一次直通率，该数量可自主配置）
//        // 自系统上线时间开始，该编码加工数量默认为0
//        // 以该编码的SN为统计维度，首次过站（FIRST_PASS=0）、工序组为SMT为筛选条件筛选数据
//        // 在上一步数据基础上，BT_FLAG为空情况下，数量+1； BT_FLAG非空的情况下（B或T），BT__FLAG = B，数量+1或BT__FLAG = T，数量+1
//        // 若该编码加工面数量>= 1000，则进入下一步计算直通率。数量1000若修改，则自动刷新为修改后的数量
//        final String strDate =  "2023-01-01 00:00:00";
//        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
//        Date startDate = sdf.parse(strDate);
//        for (int i = 0; i < codeList.size(); i++) {
//            String code = codeList.get(i);
//            int errCount = 0;
//            int existStartRow = 0;
//            SfcRate sfcRateOld = sfcRateDao.getByCode(code);
//            if(sfcRateOld != null && sfcRateOld.getStartRowNumber() != null){
//                existStartRow = sfcRateOld.getStartRowNumber();
//            }
//            List<SfcScdpMSnDetail> sfcScdpMSnDetailList = sfcScdpMSnDetailDao.getByLimit(code,existStartRow,startDate);//首次过站（FIRST_PASS=0）、工序组为SMT为筛选条件筛选数据
//            Map<String,Integer> serialNumberMap = new HashMap<>();
//            if (sfcScdpMSnDetailList.size() >= qty)
//            {
//                Integer sumCount = 0;
//                int startRowNumber = 0;
//                Integer rowNum = null;
//                for (startRowNumber = 0; startRowNumber < sfcScdpMSnDetailList.size(); startRowNumber++)
//                {
//                    rowNum = sfcScdpMSnDetailList.get(startRowNumber).getRowNum();
//                    System.out.println("rowNum:" + rowNum);
//
//                    String serialNumber = sfcScdpMSnDetailList.get(startRowNumber).getSerialNumber();
//                    String errFlag = sfcScdpMSnDetailList.get(startRowNumber).getErrFlag();
//                    String firstPass = sfcScdpMSnDetailList.get(startRowNumber).getFirstPass();
//                    if(serialNumberMap.containsKey(serialNumber)){
//                        if(serialNumberMap.get(serialNumber) == 1){
//                            sumCount++;
//                            serialNumberMap.put(serialNumber,2);
//                            if (errFlag.equalsIgnoreCase("F") && firstPass.equals("0")) {
//                                errCount++;
//                            }
//                        }
//                    }
//                    else {
//                        sumCount++;
//                        serialNumberMap.put(serialNumber,1);
//                        if (errFlag.equalsIgnoreCase("F") && firstPass.equals("0")) {
//                            errCount++;
//                        }
//                    }
//                    if(sumCount.equals(qty) ){//满足1000
//                        DecimalFormat df = new DecimalFormat("#.00");
//                        double rate0 = (1 - errCount * 1.0 / qty) * 1.0 * 100;
//                        String rate = df.format(rate0);
//
//                        SfcRate sfcRate = new SfcRate();
//                        sfcRate.setRate(rate);
//                        sfcRate.setCode(code);
//                        sfcRate.setCreateTime(new Date());
//                        sfcRate.setStartRowNumber(rowNum);
//                        sfcRate.setDenominator(qty);
//                        sfcRate.setMolecule(errCount);
//                        int count = sfcRateDao.insert(sfcRate);
//                        sumCount = 0;
//                        serialNumberMap = new HashMap<>();
//                    }
//                }
//
//                if(sumCount < qty){
//                    continue;
//                }
//            }
//            else{
//                continue;
//            }
//        }
//
//    }

    @Override
    public void scheduleFillPId() {
        final List<PcbCapacityDataApprove> byLimit = pcbCapacityDataApproveDao.getByLimit(null);
        if (byLimit == null) {
            return;
        }
        for (int i = 0; i < byLimit.size(); i++) {
            final PcbCapacityDataApprove pcbCapacityDataApprove = byLimit.get(i);
            if (pcbCapacityDataApprove.getParentFactoryId() == null) {
                final PcbSonFactoryInfo byId = pcbSonFactoryInfoDao.getById(pcbCapacityDataApprove.getFactoryId());
                if (byId != null) {
                    pcbCapacityDataApprove.setParentFactoryId(byId.getParentId());
                    pcbCapacityDataApproveDao.update(pcbCapacityDataApprove);
                }

            }
        }
    }
}
